﻿using System;
using System.Collections;
using GAIQ;

namespace DCIMAP.GANS.GeneticAlgorithms.Legacy
{
    
    [Serializable]
    public class TreeGene: ArrayGene
    {
        /// <summary>
        /// Ген представляет собой арифметическое выражение, записанное в постфиксной нотации.
        /// Таким образом его можно рассматривать как дерево.
        /// Физически он хранится как массив строк.
        /// </summary>
        private readonly string[] code;

        
        public TreeGene(int length)
        {
            code = new string[length];
        }

        
        public string[] NodeCode
        {
            get { return code; }
        }

        public int getLength() {
            return code.Length;
        }

        /// <inheritdoc/>
        public override Array Code
        {
            get { return code; }
        }

        /// <summary>
        /// Преобразует постфиксную запись в инфиксную. 
        /// </summary>
        /// <returns></returns>
        public string prettyPrint()
        {
            return helpPrettyPrint(NodeCode.Length - 1);
        }
        private string helpPrettyPrint(int root)
        {
            if (isLeaf(root))
            {
                return NodeCode[root];
            }
            else
            {
                string rightTree = helpPrettyPrint(root - 1);
                int leftRoot = getSubtree(root - 1)[0] - 1;
                string leftTree = helpPrettyPrint(leftRoot);
                return "(" + leftTree + NodeCode[root] + rightTree + ")";
            }
        }
        /// <summary>
        /// Выделяет  в массиве границы поддерева с корнем Node
        /// </summary>
        /// <param name="Node"></param>
        /// <returns>Левая и правая граница поддерева в массиве</returns>
        public int[] getSubtree(int Node)
        {
            int[] result = new int[2];
            if (isLeaf(Node))
            {
                result[0] = Node;
                result[1] = Node;
            }
            else
            {
                int[] leftSubtree = getSubtree(Node - 1);
                int[] rightSubtree = getSubtree(leftSubtree[0] - 1);
                result[0] = rightSubtree[0];
                result[1] = Node;
            }
            return result;

        }
        public bool isLeaf(int Node)
        {
            return !ListOp.isOp(NodeCode[Node]);
        }
        /// <summary>
        ///  Определяет ар-ность функции, закодированной геном. true - бинарная ( F(x,y) ), false - унарная ( F(x) )  
        /// </summary>
        public bool args()
        {
            int n = NodeCode.Length;
            bool f1 = false, f2 = false;
            for (int i = 0; i < n; i++)
            {
                switch (NodeCode[i])
                {
                    case "x":
                        f1 = true;
                        break;
                    case "y":
                        f2 = true;
                        break;
                }
                if (f1 && f2)
                    return true;
            }
            return false;
        }
        /// <summary>
        /// Вычисляет функцию, закодированную геном для конкретных параметров arg1, arg2
        /// </summary>
        public int count(int arg1, int arg2)
        {
            Stack stack = new Stack();
            int n = NodeCode.Length;
            int rez = System.Int32.MaxValue;
            try
            {
                for (int i = 0; i < n; i++)
                {
                    switch (NodeCode[i])
                    {
                        case "x":
                            stack.Push(arg1);
                            break;
                        case "y":
                            stack.Push(arg2);
                            break;
                        default:
                            if (!ListOp.isOp(NodeCode[i]))
                                stack.Push(Convert.ToInt32(NodeCode[i]));
                            else
                            {
                                int b = (int)stack.Pop();
                                int a = (int)stack.Pop();
                                if (NodeCode[i].Equals("/") && (b == 0 || (a % b) != 0))
                                    return System.Int32.MaxValue;
                                rez = ListOp.getOp(NodeCode[i]).eval(a, b);
                                stack.Push(rez);
                            }
                            break;
                    }
                }
            }
            catch (System.OverflowException e)
            {
                return System.Int32.MaxValue;
            }
            return rez;
        }
        public override bool AreIdentical(Gene _g)
        {
            TreeGene g = (TreeGene)_g;
            if (g.Code.Length != Code.Length)
                return false;
            return base.AreIdentical(_g);
        }
        public override Gene CreateEmpty()
        {
            return new TreeGene(code.Length);
        }
    }
}